﻿using System;
using System.Collections.Generic;
using System.AddIn.Pipeline;
using ROOTNET.Interface;
using ROOTNET.Utility;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using System.Text;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Media;

namespace RootfusAddinView
{
    [AddInBase]
    public abstract class SICallback
    {
        public abstract void UpdateSaveInformation(string xmltext);
        public abstract ROOTObjectSpec[] GetConnectedPinDataAsROOTObject(PinDefinition pin);
        public abstract void OutputPinsChanged();

        /// <summary>
        /// Returns the names of the connected pins. Not to be used for
        /// anything but UI.
        /// </summary>
        /// <param name="pinDefinition"></param>
        /// <returns></returns>
        public abstract string[] GetNamesOfConnectedPins(PinDefinition pinDefinition);

        /// <summary>
        /// Returns all the input pins that are allowed for this guy, or the null list.
        /// </summary>
        /// <param name="pin"></param>
        /// <returns></returns>
        public NTObject[] GetConnectedPinDataAsTObjects(PinDefinition pin)
        {
            ROOTObjectSpec[] result = GetConnectedPinDataAsROOTObject(pin);
            if (result == null)
            {
                return new NTObject[0];
            }
            List<NTObject> result_objects = new List<NTObject>();
            foreach (var obj in result)
            {
                if (obj != null)
                {
                    ROOTObjectInfo translated_obj_base = ROOTObjectManager.instance().FindROOTObject(obj._native_pointer);
                    if (translated_obj_base != null)
                    {
                        NTObject translated_obj = translated_obj_base.GetNETObject() as NTObject;
                        if (translated_obj != null)
                        {
                            result_objects.Add(translated_obj);
                        }
                    }
                }
            }
            return result_objects.ToArray();
        }

        /// <summary>
        /// Returns only the first guy that is connected to this pin. Or null! :-)
        /// </summary>
        /// <param name="pin"></param>
        /// <returns></returns>
        public NTObject GetConnectedPinDataAsTObject(PinDefinition pin)
        {
            NTObject[] result = GetConnectedPinDataAsTObjects(pin);
            if (result == null)
            {
                return null;
            }
            if (result.Length == 0)
            {
                return null;
            }
            return result[0];
        }

        /// <summary>
        /// Convert a simple settings object to XML and send it up to the app!
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="settingsObj"></param>
        public virtual void UpdateSaveObject<T>(T settingsObj)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));
            StringBuilder bld = new StringBuilder();

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.OmitXmlDeclaration = true;
            settings.Indent = false;
            XmlWriter writer = XmlWriter.Create(bld, settings);
            ser.Serialize(writer, settingsObj);
            UpdateSaveInformation(bld.ToString());
        }
        /// <summary>
        /// Convert from XML to the settings object.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public virtual T ConvertXMLToSettings<T>(string xml)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));
            return (T) ser.Deserialize(new StringReader(xml));
        }

        /// <summary>
        /// Turn the current WPF control into an image...
        /// </summary>
        /// <param name="target"></param>
        /// <param name="dpiX"></param>
        /// <param name="dpiY"></param>
        /// <returns></returns>
        private BitmapSource CreateBitmapFromVisual(UIElement target, Double dpiX, Double dpiY)
        {
            if (target == null)
            {
                return null;
            }

            Rect bounds = VisualTreeHelper.GetContentBounds(target);
            if (bounds.Width == double.NegativeInfinity || bounds.Height == double.NegativeInfinity)
            {
                bounds = new Rect(0, 0, (target as FrameworkElement).ActualWidth, (target as FrameworkElement).ActualHeight);
            }

            RenderTargetBitmap rtb = new RenderTargetBitmap((Int32)(bounds.Width * dpiX / 96.0),
                (Int32)(bounds.Height * dpiY / 96.0),
                dpiX,
                dpiY,
                PixelFormats.Pbgra32);

            DrawingVisual dv = new DrawingVisual();
            using (DrawingContext dc = dv.RenderOpen())
            {
                VisualBrush vb = new VisualBrush(target);
                dc.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
            }

            rtb.Render(dv);

            return rtb;
        }

        /// <summary>
        /// Call when you want to use an image rather than the actual WPF control. This is especially
        /// important for the first version where there is the "airspace" problem with add-ins.
        /// </summary>
        /// <param name="ui"></param>
        public void UpdateCachedImage (UIElement ui)
        {
            using (FileStream outStream = new FileStream(@"mycanvas.png", FileMode.Create))
            {
                PngBitmapEncoder enc = new PngBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(CreateBitmapFromVisual(ui, 96, 96)));

                enc.Save(outStream);
            }
        }
    }

    [Serializable]
    public class ROOTObjectSpec
    {
        public string _class_name;
        public int _native_pointer;
    }
}
